package com.wang.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class Server {

	private static final ByteBuffer BUFFER = ByteBuffer.allocate(1024);
	
/*
 * 使用SelectorIO来处理多Channel的好处是: 只需要更少的线程来处理多个IO(事实上可以只用一个线程处理所有通道);
 *  因为对于操作系统而言,多个线程间上下文切换的开销很大,而且每个线程都要占用系统的部分资源(如线程私有内存等),因此使用的线程越少越好. 
 *  但现代操作系统和CPU在多任务方面的表现越来越好,而且CPU多核已成事实, 所以线程开销随着时间的推移变得越来越小,
 *  因此不使用多任务可能就是在浪费CPU,而且NIO开发相对繁琐, 因此在使用or不使用Selector还需要做进一步的权衡.
 */
	
	public static void main(String[] args) throws Exception{
		//selector 非阻塞IO核心, 是SelectableChanel的多路复用器, 所有希望采用非阻塞方式通信的Channel
		//都需要注册到Selector, 然后Selector可以同时阻塞监听多个Channel:		
		Selector selector = Selector.open();
		
		//SelectableChanel 代表可以支持非阻塞的Channel对象,默认阻塞,必须开启非阻塞模式才可利用非阻塞特性,
		//被注册到Selector上,并由SelectionKey代表这种注册关系:		
		ServerSocketChannel server = ServerSocketChannel.open();
		server.bind(new InetSocketAddress(8088));
		server.configureBlocking(false);
		server.register(selector, SelectionKey.OP_ACCEPT);
		
		while(selector.select()>0){
			Set<SelectionKey> keys = selector.selectedKeys();
			Iterator<SelectionKey> it = keys.iterator();
			while(it.hasNext()){
				SelectionKey key = it.next();
				
				//serverSocket 可以接收连接
				if(key.isAcceptable()){
					ServerSocketChannel serverChannel = (ServerSocketChannel)key.channel();
					SocketChannel client = serverChannel.accept();
					client.configureBlocking(false);
					client.register(selector, SelectionKey.OP_READ);								
				}
				//socket是可读的
				else if(key.isReadable()){
					
					SocketChannel clientChannel = (SocketChannel)key.channel();
					String content = readFull(clientChannel);
					
					if(!content.isEmpty()){
						System.out.println(content);
						ByteBuffer buffer = ((ByteBuffer)BUFFER.clear()).put(String.format("hello %s", content).getBytes());
						for(SelectionKey akey : selector.keys()){
							SelectableChannel channel = akey.channel();
							if(channel instanceof SocketChannel){
								((SocketChannel) channel).write((ByteBuffer)buffer.flip());
							}
						}
					}else{
						key.cancel();
					}					
				}		
				//注意每次迭代末尾的iterator.remove()调用.Selector自己不会移除已触发过的SelectionKey实例,必须在处理完
				//SelectableChanel时自己移除, 等下次该Channel就绪时, Selector会再次将其放入已触发的Key中.
				it.remove();
			}			
		}
	}
	
	private static String readFull(SocketChannel client) throws IOException{
		
		StringBuffer sb = new StringBuffer();
		BUFFER.clear();
		while(client.read(BUFFER)>0){
			sb.append(StandardCharsets.UTF_8.decode(BUFFER));
			BUFFER.clear();
		}
		return sb.toString();		
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}
